#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LINE_MAX 1024
#define ARGV_MAX 20

#define skipSpace(begin) do{\
                            while (isspace(*begin)) ++begin; \
                        }while (0)
 

typedef enum operator_echo
{
    NONE_REDIR = 0, // 无输出输出标识
    INPUT_REDIR, // 输入重定向
    OUTPUT_REDIR, // 输出重定向
    APPEND_REDIR // 追加重定向
}operator_echo;

char lineComand[LINE_MAX];
char* myargv[ARGV_MAX];
int sigvalue = 0;
int lastCode = 0;

operator_echo redirType = NONE_REDIR;
char* echo_file = NULL;

void commandCheck(char* command)
{
    assert(command);
    char* begin = command;
    char* end = command + strlen(command);

    while (begin != end)
    {
        if (*begin == '>')
        {
            *begin = '\0';
            ++begin;
            if (*begin == '>')
            {
                redirType = APPEND_REDIR;
                ++begin;
            }
            else 
            {
                redirType = OUTPUT_REDIR;
            }
            skipSpace(begin);
            echo_file = begin;
            break;
        }
        else if (*begin == '<')
        {
            *begin = '\0';
            ++begin;
            redirType = INPUT_REDIR;
            skipSpace(begin);
            echo_file = begin;
            break;
        }
        else 
            ++begin;
    }
}

void openFile(int dup_file, int open_flag)
{
    int fd = 0;
    if (redirType == APPEND_REDIR || redirType == OUTPUT_REDIR)
    {
        umask(0);

#ifdef DEBUG 
        printf("%s\n", echo_file);
        printf("opne_flag = %d dup_file = %d", open_flag, dup_file);
#endif 

        fd = open(echo_file, open_flag, 0666);
    }
    else 
    {
        fd = open(echo_file, open_flag);
    }

    if (fd < 0)
    {
        perror("open");
        exit(errno);
    }

    dup2(fd, dup_file);
}

int main()
{
    while (1)
    {
        
        redirType = NONE_REDIR;
        echo_file = NULL;
        printf("[使用者@主机名 当前文件]$ ");
        fflush(stdout);

        //接收指令
        char *str = fgets(lineComand, sizeof(lineComand)-1, stdin);
        assert(str != NULL);
        lineComand[strlen(lineComand)-1] = 0;
        
        commandCheck(lineComand);

        //切割命令
        myargv[0] = strtok(lineComand, " ");
        int i = 1;
        if (myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
        {
            myargv[i++] = (char*)"--color=auto";
        }
        while (myargv[i++] = strtok(NULL, " "));
        
        if (myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if (myargv[1] != NULL)
                chdir(myargv[1]);
            continue;
        }

        if (myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if (strcmp(myargv[1], "$?") == 0)
                printf("%d %d\n", lastCode, sigvalue);
            else 
                printf("%s\n", myargv[1]);
            continue;
        }

        //创建子进程
        pid_t id = fork();
        assert( id != -1 );

        if (id == 0)
        {
            
            switch(redirType)
            {
                case NONE_REDIR:
                    break;
                case INPUT_REDIR:
                    openFile(0, O_RDONLY);
                    break;
                case OUTPUT_REDIR:
                    openFile(1, O_WRONLY | O_CREAT | O_TRUNC);
                    break;
                case APPEND_REDIR:
                    openFile(1, O_WRONLY | O_CREAT | O_APPEND);
                    break;
                default:
                    break;
            }

            execvp(myargv[0], myargv);
            exit(1);
        } 
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert( ret > 0 );
        sigvalue = ((status>>8) & 0xff);
        lastCode = (status & 0x7f);

    }

    return 0;
}
